// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImplSingleNodeBinding.
//   $Id$



#include "stdafx.h"
#include "formcontrol.h"
#include "singleNodeBinding.h"
#include "BoundNode.h"
#include "ImplBind.h"
#include "model.h"
#include "XFormsElement.h"
CImplSingleNodeBinding::CImplSingleNodeBinding(CXFormsElement* pElement)
	: CImplNodeBinding((CXFormsElement*) pElement)
//	,m_sRef(".")
//	,m_pNode(NULL)
{
	m_bisBound = true;
	m_pbsXPathExpression = &m_sRef;
	m_bApplyFirstNodeRule = true;

}

HRESULT CImplSingleNodeBinding::get_p3pType(BSTR * psType)
{
	HRESULT hr = S_FALSE;
	int i = m_vertices.GetSize();
	if(i > 0)
	{
		//no need to re-run the XPath statement, 
		//	just return the stored node.
		CVertex * vx = m_vertices.GetAt(0);
		CBoundNode * pBoundNode = vx->m_pInstanceNode;
		if(pBoundNode)
		{
			hr = pBoundNode->getP3PType(psType);
		}
	}
	return hr;
}


HRESULT CImplSingleNodeBinding::get_type(BSTR *psType)
{
	//returns a string representation of the type of the 
	//	bound data.
	HRESULT hr = S_FALSE;
	if(m_bisBound && m_vertices.GetSize() > 0)
	{
		CBoundNode * pBN = m_vertices[0]->m_pInstanceNode;
		if(pBN)
		{
			pBN->getType(psType);
			hr = psType?S_OK:S_FALSE;
		}
	}
	return hr;	
}

HRESULT CImplSingleNodeBinding::isOfType(BSTR sType, bool *pbIsType)
{
	//returns a boolean: true if sType is the same as, or an ancestor
	//	of the type of the data pointed to.

	CComBSTR sNodeType;
	HRESULT hr = get_type(&sNodeType);
	if(SUCCEEDED(hr))
	{
		if(S_FALSE == hr)
		{
			//node has no type.
			*pbIsType = false;
			return S_FALSE;
		}
		else if(sNodeType == sType)
		{
			*pbIsType = true;
			return S_OK;
		}
		else
		{
			//TODO: make ancestral.	
		}
	}
	return hr;
}


HRESULT CImplSingleNodeBinding::isOfType(BSTR sNS, BSTR sType, bool *pbIsType)
{
	//returns a boolean: true if sType is the same as, or an ancestor
	// of the type of the data pointed to.
	HRESULT hr = S_OK;
	*pbIsType = false;
	SQName DataType[1] = { {sNS, sType} };
    if(m_bisBound && m_vertices.GetSize() > 0)
	{
		CBoundNode * pBN = m_vertices[0]->m_pInstanceNode;
		if(pBN)
		{
			*pbIsType = pBN->isOneOfType(DataType, 1);
		}
	}

	return hr;
}

void CImplSingleNodeBinding::disconnect(CBoundNode* pNode)
{
	//HACK: should probably check to see if correct node
	// is being detached.
	long lUBound = m_vertices.GetUpperBound();
	if(lUBound != -1)
	{
		CBoundNode * pBN = m_vertices.GetAt(0)->m_pInstanceNode;
		if(m_OldNodes.GetUpperBound() > -1)
		{
			CBoundNode * pBN2 = m_OldNodes.GetAt(0);
			if(pBN2)
			{
				pBN2->Release();
				m_OldNodes.RemoveAt(0);
			}
		}
		if(pBN)
		{
			pBN->AddRef();
			m_OldNodes.SetAtGrow(0,pBN);
			m_vertices.RemoveAt(0);
		}
	}
}

HRESULT CImplSingleNodeBinding::onvaluechanged()
{
	//Dispatched in response to the value changing in the UI.
	// purpose: to reflect the UI change in the instance XML.

	HRESULT hr = S_OK;
	//This event is now fired from the model, Just refresh this control.
//	hr = refresh();

	//Notify listeners of the event
	__super::onvaluechanged();
	return hr;
}


HRESULT CImplSingleNodeBinding::putValue(BSTR const  sValue)
{
	//Dispatched in response to the value changing in the UI.
	// purpose: to reflect the UI change in the instance XML.

	HRESULT hr = S_OK;

	if(SUCCEEDED(hr))
		hr = PutBoundData(sValue);
	if(m_pModel)
		hr = m_pModel->recalculate(false);
	
	return hr;
}

HRESULT CImplSingleNodeBinding::getValue(BSTR * psVal) {
	return GetBoundData(psVal);
}


void CImplSingleNodeBinding::getInstanceNode(IXMLDOMNode ** ppNode)
{
	int i = m_vertices.GetSize();
	if(i > 0)
	{
		//no need to re-run the XPath statement, 
		//	just return the stored node.
		CVertex * vx = m_vertices.GetAt(0);
		CBoundNode * pBoundNode = vx->m_pInstanceNode;
		if(pBoundNode)
		{
			pBoundNode->getNode(ppNode);
			pBoundNode->UpdateIndividualTarget(this);
		}
	}

}


HRESULT CImplSingleNodeBinding::GetBoundData(BSTR * psData)
{
	
	//TODO: check error codes, they should probably be E_FAIL.

	//To become obsolete when Core module arrives
	HRESULT hr = S_OK;

	if(m_bisBound && m_vertices.GetSize() > 0)
	{
		CBoundNode * pBoundNode = m_vertices.GetAt(0)->m_pInstanceNode;
		if(pBoundNode)
		{
			pBoundNode->UpdateIndividualTarget(this);
			pBoundNode->getValue(psData);
		}
	/*	else if(m_sRef)
		{
			CComPtr<IXMLDOMNode> pContext;
			//is a vertex without a node, just evaluate the XPath
			GetEvaluationContext(&pContext);
			hr = m_pModel->getInstanceString(m_sRef,pContext,psData);
		}
	*/}
	else if(m_sRef && m_pModel && m_bCanBindToValue)
	{
		m_pXForms->putXPathIdentityResolver(GetIdResolver());
		//it is a control that binds to non-node (this is not allowed.)
		//Except for output where value is set.
		//FUDGE: value is just copied into ref.
		CComPtr<IXMLDOMNode> pContext;
		GetEvaluationContext(&pContext);
		hr = m_pModel->getInstanceString(m_sRef,pContext,psData);
		m_pXForms->putXPathIdentityResolver(0);
	}
	else
	{
		*psData = 0;
	}

	return hr;
}

HRESULT CImplSingleNodeBinding::PutBoundData(BSTR const sValue)
{

	//S_FALSE means control is bound to a node that does not exist.
	HRESULT hr = S_FALSE;
	int i = m_vertices.GetSize();
	if(i > 0)
	{
		CBoundNode * pBoundNode = m_vertices.GetAt(0)->m_pInstanceNode;
		if(pBoundNode)
		{
			hr = pBoundNode->setValue(sValue);
			if(FAILED(hr))
			{
				//This could have failed because the node is read-only
				// or for other reasons, in any case, we need to refresh the 
				//	control.
				hr = refresh();
			}
			else
			{
				//The value has been successfully inserted.  This means
                //	that we have to run a recalculate to flush the pertinent
				//	DependencySubGraph.
			}
		}
		else
		{
			//Control is bound to NOT_A_NODE; this is an error
			hr = E_FAIL;
		}
	}

	return hr;
}

HRESULT CImplSingleNodeBinding::refresh()
{
	HRESULT hr = S_OK;
#if DEBUG_LEVEL & DEBUG_MONITOR_EVENTS
	AtlTrace("'formsplayer.dll': called CImplSingleNodeBinding::refresh on %X, %S\n",this,m_sId);
#endif	
	if(m_pXFormsElement)
	{

		//get the value from the data and send it to the 
		// front end
		CComBSTR sData;
		hr = GetBoundData(&sData);
		if ((S_FALSE == hr) || !sData)
			sData = _T("");

		hr = m_pXFormsElement->put_value(sData.m_str);		
	}

	POSITION pos = m_ReliantControls.GetHeadPosition();
	while(pos)
	{
		if(FAILED(m_ReliantControls.GetNext(pos)->refresh()))
			return E_FAIL;
	}
	
	FireStateEvents();

	return hr;
}
